package de.geraldschenke.tictactoe.server;

import java.lang.ref.WeakReference;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import de.geraldschenke.tictactoe.common.GameProperties;
import de.geraldschenke.tictactoe.common.PlayerEvent;
import de.geraldschenke.tictactoe.common.RemotePlayer;

public class GameThread extends Thread {
	public PlayerEventHandler mHandler;
	private Game mGame;
	private boolean mInitialized;
	

	// static handler class to prevent memory leaks
	static class PlayerEventHandler extends Handler {
		WeakReference<Game> mGame;

		PlayerEventHandler(Game game) {
			mGame = new WeakReference<Game>(game);
		}

		@Override
		public void handleMessage(Message message) {
			if (!(message.obj instanceof PlayerEvent)){
				Log.e(GameThread.class.getSimpleName(), "Unknown message type! "+message);
				return;
			}
			Game game = mGame.get();
			PlayerEvent playerEvent = (PlayerEvent) message.obj;
			switch (message.what) {
			case 0: //PlayerEvent.Type.SET
				if (game.getFieldOwner(playerEvent.x, playerEvent.y) == null) {
					game.makeAMove(playerEvent.x, playerEvent.y);
				}
				game.validateBoard();
				game.broadcastUpdate();
				break;
			}
		}
	}

	public void run() {
		Looper.prepare();
		mGame = new Game();
		mHandler = new PlayerEventHandler(mGame);
		mInitialized = true;
		Looper.loop();
	}

	/**
	 * this will put the event in the queue if a game is running and return
	 * instantly
	 * 
	 * ignores messages, if game is not running!
	 * 
	 * @param playerEvent
	 */
	public synchronized void handlePlayerEvent(final PlayerEvent playerEvent) {
		if (mGame.isRunning) {
			Message m = Message.obtain(mHandler, playerEvent.type.ordinal(), playerEvent);
			mHandler.sendMessage(m);
		} else {
			// throw exception?
			Log.w(GameThread.class.getSimpleName(), "No running game! Ignoring playerEvent: " + playerEvent);
		}
	}

	/**
	 * This method is allowed to be called from any thread
	 */
	public synchronized void requestStop() {
		// using the handler, post a Runnable that will quit()
		// the Looper attached to our GameThread
		// obviously, all previously queued tasks will be executed
		// before the loop gets the quit Runnable
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				// This is guaranteed to run on the GameThread
				// so we can use myLooper() to get its looper
				Log.i(GameThread.class.getSimpleName(), "GameThread loop quitting by request");
				Looper.myLooper().quit();
			}
		});
	}

	/**
	 * Registers a new player in the game
	 *  
	 * @param remotePlayer
	 * @return id of registered player
	 */
	public synchronized int registerPlayer(RemotePlayer remotePlayer) {
		Log.d(GameThread.class.getSimpleName(), "registerPlayer(" + remotePlayer + ")");
		return mGame.registerPlayer(remotePlayer);
	}

	public synchronized void startGame(GameProperties gameProperties) {
		Log.d(GameThread.class.getSimpleName(), "initGame(" + gameProperties + ")");
		mGame.initAndStart(gameProperties);
	}


	public synchronized void resetGame() {
		Log.d(GameThread.class.getSimpleName(), "resetGame()");
		mGame.reset();
	}

	public synchronized void setInitialized(boolean b) {
		Log.d(GameThread.class.getSimpleName(), "setInitialized()");
		mInitialized = b;
	}

	public synchronized boolean isInitialized() {
		Log.d(GameThread.class.getSimpleName(), "isInitialized()");
		return mInitialized;
	}
	
	public synchronized boolean isGameRunning() {
		Log.d(GameThread.class.getSimpleName(), "isGameRunning()");
		return mGame.isRunning && !mGame.isBoardEmpty();
	}

	public synchronized void registerObserver(RemotePlayer remotePlayer) {
		Log.d(GameThread.class.getSimpleName(), "registerObserver(" + remotePlayer + ")");
		mGame.registerObserver(remotePlayer);
	}

}